package com.qztech.bluetooth;


import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.inuker.bluetooth.library.BluetoothClient;
import com.inuker.bluetooth.library.Constants;
import com.inuker.bluetooth.library.connect.listener.BleConnectStatusListener;
import com.inuker.bluetooth.library.connect.listener.BluetoothStateListener;
import com.inuker.bluetooth.library.connect.options.BleConnectOptions;
import com.inuker.bluetooth.library.connect.response.BleConnectResponse;
import com.inuker.bluetooth.library.connect.response.BleNotifyResponse;
import com.inuker.bluetooth.library.model.BleGattCharacter;
import com.inuker.bluetooth.library.model.BleGattProfile;
import com.inuker.bluetooth.library.model.BleGattService;
import com.inuker.bluetooth.library.search.SearchRequest;
import com.inuker.bluetooth.library.search.SearchResult;
import com.inuker.bluetooth.library.search.response.SearchResponse;
import com.inuker.bluetooth.library.utils.BluetoothLog;
import com.inuker.bluetooth.library.utils.ListUtils;
import com.qztech.bluetooth.event.BleConnectFailedEvent;
import com.qztech.bluetooth.event.BleConnectStatusEvent;
import com.qztech.bluetooth.event.BluetoothStateEvent;
import com.qztech.bluetooth.event.DeviceFoundedEvent;
import com.qztech.bluetooth.event.NotifyDataEvent;
import com.qztech.bluetooth.event.SearchStateEvent;
import com.qztech.bluetooth.util.BLog;
import com.qztech.bluetooth.util.ByteUtil;
import com.qztech.utils.YzDebug;

import org.greenrobot.eventbus.EventBus;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;


public class BluetoothClientManager {

    private static BluetoothClientManager INSTANCE;

    private BluetoothClient mClient;

    private SearchStateEvent mSearchStateEvent;

    /**
     * 已发现的设备列表
     */
    private List<SearchResult> mFoundDeviceList = new ArrayList<>();

    /**
     * 已发现的设备地址
     */
    private Set<String> mFoundDeviceMac = new HashSet<>();

    /**
     * 已连接设备列表
     */
//    private Map<String, ZBtDevice> mConnectDeviceList = new HashMap<>();
    private ZBtDevice mConnectedDevice;

    private boolean readData = false;

    public boolean isReadData() {
        return readData;
    }

    /**
     * 发送线程
     */
    private Map<String, WriteThread> mWriteThreadList = new HashMap<>();

    public static BluetoothClientManager getInstance(Context context) {
        if (INSTANCE == null) {
            synchronized (BluetoothClientManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new BluetoothClientManager(context);
                }
            }
        }
        return INSTANCE;
    }

    private BluetoothClientManager(Context context) {
        mSearchStateEvent = new SearchStateEvent(SearchStateEvent.SEARCH_DEFAULT);
        mClient = new BluetoothClient(context.getApplicationContext());
        registers();
    }

    private void registers() {
        mClient.registerBluetoothStateListener(mBluetoothStateListener);
//        mClient.registerConnectStatusListener(mBleConnectStatusListener);
    }

    private BluetoothStateListener mBluetoothStateListener = new BluetoothStateListener() {
        @Override
        public void onBluetoothStateChanged(boolean openOrClosed) {
            EventBus.getDefault().post(new BluetoothStateEvent(openOrClosed));
        }
    };

    private class MyBleConnectStatusListener extends BleConnectStatusListener {

        @Override
        public void onConnectStatusChanged(String mac, int status) {
            if (status == Constants.STATUS_DISCONNECTED) {
                if (mConnectedDevice == null) {
                    return;
                }
                if (mConnectedDevice != null && mac.equals(mConnectedDevice.getMac())) {
                    WriteThread writeThread = mWriteThreadList.get(mac);
                    if (writeThread != null) {
                        writeThread.stopWrite();
                        mWriteThreadList.remove(mac);
                    }
                    ZBtDevice zBtDevice = new ZBtDevice();
                    zBtDevice.setName(mConnectedDevice.getName());
                    zBtDevice.setMac(mConnectedDevice.getMac());
                    mConnectedDevice = null;
                    EventBus.getDefault().post(new BleConnectStatusEvent(zBtDevice, true, status));
                }
            }
        }
    }


    public void destory() {
        for (WriteThread thread : mWriteThreadList.values()) {
            thread.stopWrite();
        }
        mWriteThreadList.clear();
        mClient.unregisterBluetoothStateListener(mBluetoothStateListener);
//        mClient.unregisterConnectStatusListener(mBleConnectStatusListener);
    }

    /**
     * 蓝牙是否开启
     *
     * @return
     */
    public boolean isBluetoothOpened() {
        return mClient.isBluetoothOpened();
    }

    public boolean openBluetooth() {
        return mClient.openBluetooth();
    }

    /**
     * 连接BLE
     *
     * @param zBtDevice
     */
    public void connectLeDevice(final ZBtDevice zBtDevice) {
        BleConnectOptions options = new BleConnectOptions.Builder().setConnectRetry(2).setConnectTimeout(5000).setServiceDiscoverRetry(2).setServiceDiscoverTimeout(8000).build();
        mClient.connect(zBtDevice.getMac(), options, new BleConnectResponse() {
            @Override
            public void onResponse(int code, BleGattProfile data) {
                BLog.d("connect", "code= " + code);
                BluetoothLog.v(String.format("profile:\n%s", new Object[]{data}));
                if (code == Constants.REQUEST_SUCCESS) {
                    if (mConnectedDevice != null && mConnectedDevice.getMac().equalsIgnoreCase(zBtDevice.getMac()))return;
                    BleGattService service = data.getService(zBtDevice.getServiceUuid());
                    if (service == null) {
                        EventBus.getDefault().post(new BleConnectStatusEvent(zBtDevice, false, Constants.STATUS_DISCONNECTED));
                        mClient.disconnect(zBtDevice.getMac());
                        return;
                    }
                    List<BleGattCharacter> characters = service.getCharacters();
                    if (!ListUtils.isEmpty(characters)) {
                        int uuidCount = zBtDevice.getUuidCount();
                        int currentUuidCount = 0;
                        for (BleGattCharacter character : characters) {
                            if (zBtDevice.getWriteCharacterUuid() != null && zBtDevice.getWriteCharacterUuid().equals(character.getUuid())) {
                                currentUuidCount++;
                            }
                            if (zBtDevice.getReadCharacterUuid() != null && zBtDevice.getReadCharacterUuid().equals(character.getUuid())) {
                                currentUuidCount++;
                            }
                            if (zBtDevice.getNotifyCharacterUuid() != null && zBtDevice.getNotifyCharacterUuid().equals(character.getUuid())) {
                                currentUuidCount++;
                            }
                        }
                        if (currentUuidCount == uuidCount) {
                            zBtDevice.setStatus(Constants.STATUS_CONNECTED);
                            zBtDevice.setBleConnectStatusListener(new MyBleConnectStatusListener());
                            mConnectedDevice = zBtDevice;
                            WriteThread writeThread = new WriteThread(mClient, zBtDevice);
                            writeThread.start();
                            mWriteThreadList.put(zBtDevice.getMac(), writeThread);
                            mClient.registerConnectStatusListener(zBtDevice.getMac(), zBtDevice.getBleConnectStatusListener());
                            if (zBtDevice.getNotifyCharacterUuid() != null) {
                                setNotify(zBtDevice);
                            }
                            EventBus.getDefault().post(new BleConnectStatusEvent(zBtDevice, true, Constants.STATUS_CONNECTED));
                        } else {
                            EventBus.getDefault().post(new BleConnectStatusEvent(zBtDevice, false, Constants.STATUS_DISCONNECTED));
                            mClient.disconnect(zBtDevice.getMac());
                        }
                    }
                } else {
                    mClient.refreshCache(zBtDevice.getMac());
                    mClient.disconnect(zBtDevice.getMac());
                    EventBus.getDefault().post(new BleConnectFailedEvent(zBtDevice));
                }
            }
        });

    }

    private void setNotify(ZBtDevice device) {
        mClient.notify(device.getMac(), device.getServiceUuid(), device.getNotifyCharacterUuid(), new BleNotifyResponse() {
            @Override
            public void onNotify(UUID service, UUID character, byte[] value) {
//                EventBus.getDefault().post(new NotifyDataEvent(service, character, value));
                ReceiveDataUtil.receivedData(value);
            }

            @Override
            public void onResponse(int code) {
                Log.i("notify", "responseCode : " + code);
            }
        });
    }

    /**
     * 断开连接
     */
    public void disconnect() {
        if (mConnectedDevice == null || mConnectedDevice.getStatus() != Constants.STATUS_CONNECTED) {
            return;
        }
        mClient.disconnect(mConnectedDevice.getMac());
        mClient.unregisterConnectStatusListener(mConnectedDevice.getMac(), mConnectedDevice.getBleConnectStatusListener());
        WriteThread writeThread = mWriteThreadList.get(mConnectedDevice.getMac());
        if (writeThread != null) {
            writeThread.stopWrite();
            mWriteThreadList.remove(mConnectedDevice.getMac());
        }
        mConnectedDevice = null;
    }

    /**
     * 获取已搜到的设备
     *
     * @return
     */
    public List<SearchResult> getFoundDeviceList() {
        return mFoundDeviceList;
    }

    public void setReadData(boolean readData) {
        this.readData = readData;
    }

    /**
     * @param mac
     * @param buffer
     */
    public void write(String mac, byte[] buffer) {
        if (readData && !WriteThread.isWriteWithResponse(buffer)) {
            return;
        }
        WriteThread writeThread = mWriteThreadList.get(mac);
        if (writeThread != null) {
            writeThread.add(buffer);
        }
    }

    public void write(byte[] buffer) {
        if (readData && !WriteThread.isWriteWithResponse(buffer)) {
            return;
        }
        if (mConnectedDevice == null || mConnectedDevice.getStatus() != Constants.STATUS_CONNECTED) {
            return;
        }
        WriteThread writeThread = mWriteThreadList.get(mConnectedDevice.getMac());
        if (writeThread != null) {
            writeThread.add(buffer);
        }
    }

    /**
     * 搜索BLE设备
     */
    public void searchBleDevice() {
        SearchRequest searchRequest = new SearchRequest.Builder().searchBluetoothLeDevice(5000, 1).build();
        mClient.search(searchRequest, new SearchResponse() {
            @Override
            public void onSearchStarted() {
                mFoundDeviceMac.clear();
                mFoundDeviceList.clear();
                mSearchStateEvent.setStatus(SearchStateEvent.SEARCH_START);
                EventBus.getDefault().post(mSearchStateEvent);
            }

            @Override
            public void onDeviceFounded(SearchResult device) {
                  YzDebug.d("onDeviceFounded1:"+ device);

                if (TextUtils.isEmpty(device.getName())) {
                    return;
                }
                if (!BluetoothConfig.isAvailableName(device.getName())){
                    return;
                }
                if (!mFoundDeviceMac.add(device.getAddress())) {
                    return;
                }
                mFoundDeviceList.add(device);

                EventBus.getDefault().post(new DeviceFoundedEvent(device));
            }

            @Override
            public void onSearchStopped() {
                mSearchStateEvent.setStatus(SearchStateEvent.SEARCH_STOP);
                EventBus.getDefault().post(mSearchStateEvent);
            }

            @Override
            public void onSearchCanceled() {
                mSearchStateEvent.setStatus(SearchStateEvent.SEARCH_CANCEL);
                EventBus.getDefault().post(mSearchStateEvent);
            }
        });
    }

    /**
     * 停止搜索
     */
    public void stopSearch() {
        mClient.stopSearch();
    }

    public boolean isConnect() {
        return mConnectedDevice != null && mConnectedDevice.getStatus() == Constants.STATUS_CONNECTED;
    }

    public ZBtDevice getConnectedDevice() {
        YzDebug.d("getConnectedDevice:"+ mConnectedDevice);
        return mConnectedDevice;
    }
}
